﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Automation;
using Compiled_Tests.Utils;
using System.Text.RegularExpressions;
using LEET.CommonInterfaces.Automation;
using Compiled_Tests.AutomationAbstractions;
using System.IO;
using LEET.Utilities;


namespace LEET.LEETCommon.WidgetLookup
{
    public class WidgetFinder
    {
        public static IList<AE_Identifier> parseIdentifiers(String key)
        {
            IList<AE_Identifier> ids = new List<AE_Identifier>();
            String[] keys = key.Split(',');
            foreach (String tuple in keys)
            {
                Match m = Regex.Match(tuple, " *\\(\"(.*?)\"; \"(.*?)\"; \"(.*?)\"\\)");
                if (m.Success)
                {
                    AE_Identifier id = new AE_Identifier();
                    id.id = m.Groups[1].Value;
                    id.name = m.Groups[2].Value;
                    id.klass = m.Groups[3].Value;
                    ids.Add(id);
                }
                else throw new InvalidIdentifierException();
            }
            return ids;
        }

        public static AutomationElement Find(String path, AutomationElement rootOfSearch)
        {
            try
            {
                return new WidgetFinder().getElement(path, rootOfSearch);

            }
            catch (InvalidIdentifierException e)
            {
                // search by id
                return rootOfSearch.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.AutomationIdProperty, path));
            }
        }

        public void FindElement(IUIPath path, Lookup<AutomationElement> lookup)
        {
            if (path.head == null)
            {
                lookup.Complete();
            }
            else
            {
                AutomationElementCollection options = FindChildren(path.head, lookup.Location);
                if (options == null)
                {
                    options = lookup.Location.FindAll(TreeScope.Children, Condition.TrueCondition);
                    List<AutomationElement> list = new List<AutomationElement>();
                    foreach(AutomationElement e in options)list.Add(e);
                    lookup.Fail(list);
                }
                else
                {
                    for (int i = 0; i < options.Count; i++)
                    {
                        AutomationElement possible = options[i];
                        lookup.Include(possible);
                        FindElement(path.tail, lookup);
                    }
                }
                
            }
        }

        private AutomationElementCollection FindChildren(IUIPathElement elem, AutomationElement parent)
        {
            AutomationElementCollection next = null;
            Condition andCondition;

            List<Condition> conditions = new List<Condition>();
            conditions.Add(new PropertyCondition(AutomationElement.NameProperty, elem.Name));
            conditions.Add(new PropertyCondition(AutomationElement.AutomationIdProperty, elem.ID));
            conditions.Add(new PropertyCondition(AutomationElement.ClassNameProperty, elem.Class));
            while ((next == null && conditions.Count > 0) || (next != null && next.Count <= 0))
            {
                try
                {
                    if (conditions.Count > 1)
                        andCondition = new AndCondition(conditions.ToArray());
                    else
                        andCondition = conditions[0];
                    next = parent.FindAll(TreeScope.Children, andCondition);
                    conditions.RemoveAt(0);
                }
                catch (Exception e)
                {
                    next = null;
                }
            }
            return next;
        }

        private AutomationElementCollection FindChildren2(IUIPathElement elem, AutomationElement parent)
        {
            AutomationElementCollection ret;
            List<Condition> conditions = new List<Condition>();

            // check for perfect match
            conditions.Add(
                new AndCondition(
                    new PropertyCondition(AutomationElement.NameProperty, elem.Name),
                    new PropertyCondition(AutomationElement.AutomationIdProperty, elem.ID),
                    new PropertyCondition(AutomationElement.ClassNameProperty, elem.Class)
                )
            );

            // check ID and Class only
            conditions.Add(
                new AndCondition(
                    new PropertyCondition(AutomationElement.AutomationIdProperty, elem.ID),
                    new PropertyCondition(AutomationElement.ClassNameProperty, elem.Class)
                )
            );

            // check Name and Class only
            conditions.Add(
                new AndCondition(
                    new PropertyCondition(AutomationElement.NameProperty, elem.Name),
                    new PropertyCondition(AutomationElement.ClassNameProperty, elem.Class)
                )
            );

            // Shit, somethings up, lets just try Class and see whats what
            conditions.Add(
                new PropertyCondition(AutomationElement.ClassNameProperty, elem.Class)
            );

            foreach (Condition condition in conditions)
            {
                try
                {
                    ret = parent.FindAll(TreeScope.Children, condition);
                    if (ret.Count > 0)
                    {
                        return ret;
                    }
                }
                catch (Exception) { /* move on to the next condition */ }
            }

            // damn, no match. move back up the tree
            return null;
        }


        public AutomationElement getElement(string key, AutomationElement head)
        {
            if (key == null || key.Equals(""))
                return head;
            if (head == null)
                throw new Exception();


            IUIPath path = UIPath.Apply(key);
            //Let's poll for this incase the widget isn't visible yet
            // -- A big issue when opening dialogs
            Lookup<AutomationElement> lookUP = Helpers.Poll(delegate()
            {
                Lookup<AutomationElement> lookup = new Lookup<AutomationElement>(head);
                FindElement(path, lookup);
                return lookup;
            }, 100, 10000, delegate(Lookup<AutomationElement> lookup)
            {
                //if the lookup fails keep polling
                return !lookup.Failed;
            });

            if (lookUP.Failed)
            {
                throw new FailedPathException("Failed to find element \"" + path.ToString() + "\" lookup yeilded Pathing: " + Environment.NewLine + lookUP.ToString(delegate(AutomationElement e) { return "\"" + e.Current.AutomationId + "\""; }));
            }

            //return the item the lookup Completed at
            return lookUP.Location;

        }

        

    }
}
